/*-------------------------------------------------------------------------
 *
 * lockfuncs.c
 *		用于 SQL 访问各种锁管理功能的函数。
 *
 * Copyright (c) 2002-2022, PostgreSQL Global Development Group
 *
 * IDENTIFICATION
 *		src/backend/utils/adt/lockfuncs.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/htup_details.h"
#include "access/xact.h"
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "storage/predicate_internals.h"
#include "utils/array.h"
#include "utils/builtins.h"


/*
 * 这必须与 enum LockTagType 匹配！另请确保记录任何更改
 * 在 pg_locks 视图和等待事件类型的文档中。
 */
const char *const LockTagTypeNames[] = {
	"relation",
	"extend",
	"frozenid",
	"page",
	"tuple",
	"transactionid",
	"virtualxid",
	"spectoken",
	"object",
	"userlock",
	"advisory"
};

StaticAssertDecl(lengthof(LockTagTypeNames) == (LOCKTAG_ADVISORY + 1),
				 "array length mismatch");

/* 这必须与 enum PredicateLockTargetType (predicate_internals.h) 匹配 */
static const char *const PredicateLockTagTypeNames[] = {
	"relation",
	"page",
	"tuple"
};

StaticAssertDecl(lengthof(PredicateLockTagTypeNames) == (PREDLOCKTAG_TUPLE + 1),
				 "array length mismatch");

/* pg_lock_status 的工作状态 */
typedef struct
{
	LockData   *lockData;		/* 来自 lmgr 的状态数据 */
	int			currIdx;		/* 当前 PROCLOCK 索引 */
	PredicateLockData *predLockData;	/* 用于预测锁的状态数据 */
	int			predLockIdx;	/* 预测锁的当前索引 */
} PG_Lock_Status;

/* pg_locks 输出中的列数 */
#define NUM_LOCK_STATUS_COLUMNS		16

/*
 * VXIDGetDatum - 构建 VXID 的文本表示
 *
 * 目前这仅在 pg_lock_status 中使用，因此我们将其放在这里。
 */
static Datum fc_VXIDGetDatum(BackendId fc_bid, LocalTransactionId fc_lxid)
{
	/*
	 * 表示形式为 "<bid>/<lxid>"，分别为十进制和无符号十进制。
	 * 注意，elog.c 也知道如何格式化一个 vxid。
	 */
	char		fc_vxidstr[32];

	snprintf(fc_vxidstr, sizeof(fc_vxidstr), "%d/%u", fc_bid, fc_lxid);

	return CStringGetTextDatum(fc_vxidstr);
}


/*
 * pg_lock_status - 生成每个持有或等待锁模式的一行视图
 */
Datum pg_lock_status(PG_FUNCTION_ARGS)
{
	FuncCallContext *fc_funcctx;
	PG_Lock_Status *fc_mystatus;
	LockData   *fc_lockData;
	PredicateLockData *fc_predLockData;

	if (SRF_IS_FIRSTCALL())
	{
		TupleDesc	fc_tupdesc;
		MemoryContext fc_oldcontext;

		/* 为跨调用持久性创建一个函数上下文 */
		fc_funcctx = SRF_FIRSTCALL_INIT();

		/*
		 * 切换到适合多次函数调用的内存上下文
		 */
		fc_oldcontext = MemoryContextSwitchTo(fc_funcctx->multi_call_memory_ctx);

		/* 为结果元组构建 tupdesc */
		/* 这最好与 pg_proc.h 中函数的声明匹配 */
		fc_tupdesc = CreateTemplateTupleDesc(NUM_LOCK_STATUS_COLUMNS);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 1, "locktype",
						   TEXTOID, -1, 0);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 2, "database",
						   OIDOID, -1, 0);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 3, "relation",
						   OIDOID, -1, 0);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 4, "page",
						   INT4OID, -1, 0);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 5, "tuple",
						   INT2OID, -1, 0);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 6, "virtualxid",
						   TEXTOID, -1, 0);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 7, "transactionid",
						   XIDOID, -1, 0);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 8, "classid",
						   OIDOID, -1, 0);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 9, "objid",
						   OIDOID, -1, 0);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 10, "objsubid",
						   INT2OID, -1, 0);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 11, "virtualtransaction",
						   TEXTOID, -1, 0);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 12, "pid",
						   INT4OID, -1, 0);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 13, "mode",
						   TEXTOID, -1, 0);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 14, "granted",
						   BOOLOID, -1, 0);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 15, "fastpath",
						   BOOLOID, -1, 0);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 16, "waitstart",
						   TIMESTAMPTZOID, -1, 0);

		fc_funcctx->tuple_desc = BlessTupleDesc(fc_tupdesc);

		/*
		 * 收集所有将要格式化并作为结果集发送的锁定信息。
		 */
		fc_mystatus = (PG_Lock_Status *) palloc(sizeof(PG_Lock_Status));
		fc_funcctx->user_fctx = (void *) fc_mystatus;

		fc_mystatus->lockData = GetLockStatusData();
		fc_mystatus->currIdx = 0;
		fc_mystatus->predLockData = GetPredicateLockStatusData();
		fc_mystatus->predLockIdx = 0;

		MemoryContextSwitchTo(fc_oldcontext);
	}

	fc_funcctx = SRF_PERCALL_SETUP();
	fc_mystatus = (PG_Lock_Status *) fc_funcctx->user_fctx;
	fc_lockData = fc_mystatus->lockData;

	while (fc_mystatus->currIdx < fc_lockData->nelements)
	{
		bool		fc_granted;
		LOCKMODE	fc_mode = 0;
		const char *fc_locktypename;
		char		fc_tnbuf[32];
		Datum		fc_values[NUM_LOCK_STATUS_COLUMNS];
		bool		fc_nulls[NUM_LOCK_STATUS_COLUMNS];
		HeapTuple	fc_tuple;
		Datum		fc_result;
		LockInstanceData *fc_instance;

		fc_instance = &(fc_lockData->locks[fc_mystatus->currIdx]);

		/*
		 * 查看这个 PROCLOCK 中是否有任何持有的锁模式。如果
		 * 有，报告，并破坏性地修改 lockData，以便我们不再报告
		 * 。
		 */
		fc_granted = false;
		if (fc_instance->holdMask)
		{
			for (fc_mode = 0; fc_mode < MAX_LOCKMODES; fc_mode++)
			{
				if (fc_instance->holdMask & LOCKBIT_ON(fc_mode))
				{
					fc_granted = true;
					fc_instance->holdMask &= LOCKBIT_OFF(fc_mode);
					break;
				}
			}
		}

		/*
		 * 如果没有（更多）要报告的持有模式，查看 PROC 是否在等
		 * 待这把锁。
		 */
		if (!fc_granted)
		{
			if (fc_instance->waitLockMode != NoLock)
			{
				/* 是的，因此以适当的模式进行报告 */
				fc_mode = fc_instance->waitLockMode;

				/*
				 * 我们现在完成了这个 PROCLOCK，因此指针前进到
				 * 下一个调用继续下一个。
				 */
				fc_mystatus->currIdx++;
			}
			else
			{
				/*
				 * 好的，我们已经显示了与这个
				 * PROCLOCK 关联的所有锁，继续下一个。
				 */
				fc_mystatus->currIdx++;
				continue;
			}
		}

		/*
		 * 使用适当的数据形成元组。
		 */
		MemSet(fc_values, 0, sizeof(fc_values));
		MemSet(fc_nulls, false, sizeof(fc_nulls));

		if (fc_instance->locktag.locktag_type <= LOCKTAG_LAST_TYPE)
			fc_locktypename = LockTagTypeNames[fc_instance->locktag.locktag_type];
		else
		{
			snprintf(fc_tnbuf, sizeof(fc_tnbuf), "unknown %d",
					 (int) fc_instance->locktag.locktag_type);
			fc_locktypename = fc_tnbuf;
		}
		fc_values[0] = CStringGetTextDatum(fc_locktypename);

		switch ((LockTagType) fc_instance->locktag.locktag_type)
		{
			case LOCKTAG_RELATION:
			case LOCKTAG_RELATION_EXTEND:
				fc_values[1] = ObjectIdGetDatum(fc_instance->locktag.locktag_field1);
				fc_values[2] = ObjectIdGetDatum(fc_instance->locktag.locktag_field2);
				fc_nulls[3] = true;
				fc_nulls[4] = true;
				fc_nulls[5] = true;
				fc_nulls[6] = true;
				fc_nulls[7] = true;
				fc_nulls[8] = true;
				fc_nulls[9] = true;
				break;
			case LOCKTAG_DATABASE_FROZEN_IDS:
				fc_values[1] = ObjectIdGetDatum(fc_instance->locktag.locktag_field1);
				fc_nulls[2] = true;
				fc_nulls[3] = true;
				fc_nulls[4] = true;
				fc_nulls[5] = true;
				fc_nulls[6] = true;
				fc_nulls[7] = true;
				fc_nulls[8] = true;
				fc_nulls[9] = true;
				break;
			case LOCKTAG_PAGE:
				fc_values[1] = ObjectIdGetDatum(fc_instance->locktag.locktag_field1);
				fc_values[2] = ObjectIdGetDatum(fc_instance->locktag.locktag_field2);
				fc_values[3] = UInt32GetDatum(fc_instance->locktag.locktag_field3);
				fc_nulls[4] = true;
				fc_nulls[5] = true;
				fc_nulls[6] = true;
				fc_nulls[7] = true;
				fc_nulls[8] = true;
				fc_nulls[9] = true;
				break;
			case LOCKTAG_TUPLE:
				fc_values[1] = ObjectIdGetDatum(fc_instance->locktag.locktag_field1);
				fc_values[2] = ObjectIdGetDatum(fc_instance->locktag.locktag_field2);
				fc_values[3] = UInt32GetDatum(fc_instance->locktag.locktag_field3);
				fc_values[4] = UInt16GetDatum(fc_instance->locktag.locktag_field4);
				fc_nulls[5] = true;
				fc_nulls[6] = true;
				fc_nulls[7] = true;
				fc_nulls[8] = true;
				fc_nulls[9] = true;
				break;
			case LOCKTAG_TRANSACTION:
				fc_values[6] =
					TransactionIdGetDatum(fc_instance->locktag.locktag_field1);
				fc_nulls[1] = true;
				fc_nulls[2] = true;
				fc_nulls[3] = true;
				fc_nulls[4] = true;
				fc_nulls[5] = true;
				fc_nulls[7] = true;
				fc_nulls[8] = true;
				fc_nulls[9] = true;
				break;
			case LOCKTAG_VIRTUALTRANSACTION:
				fc_values[5] = fc_VXIDGetDatum(fc_instance->locktag.locktag_field1,
										 fc_instance->locktag.locktag_field2);
				fc_nulls[1] = true;
				fc_nulls[2] = true;
				fc_nulls[3] = true;
				fc_nulls[4] = true;
				fc_nulls[6] = true;
				fc_nulls[7] = true;
				fc_nulls[8] = true;
				fc_nulls[9] = true;
				break;
			case LOCKTAG_OBJECT:
			case LOCKTAG_USERLOCK:
			case LOCKTAG_ADVISORY:
			default:			/* 将未知锁标签视为对象 */
				fc_values[1] = ObjectIdGetDatum(fc_instance->locktag.locktag_field1);
				fc_values[7] = ObjectIdGetDatum(fc_instance->locktag.locktag_field2);
				fc_values[8] = ObjectIdGetDatum(fc_instance->locktag.locktag_field3);
				fc_values[9] = Int16GetDatum(fc_instance->locktag.locktag_field4);
				fc_nulls[2] = true;
				fc_nulls[3] = true;
				fc_nulls[4] = true;
				fc_nulls[5] = true;
				fc_nulls[6] = true;
				break;
		}

		fc_values[10] = fc_VXIDGetDatum(fc_instance->backend, fc_instance->lxid);
		if (fc_instance->pid != 0)
			fc_values[11] = Int32GetDatum(fc_instance->pid);
		else
			fc_nulls[11] = true;
		fc_values[12] = CStringGetTextDatum(GetLockmodeName(fc_instance->locktag.locktag_lockmethodid, fc_mode));
		fc_values[13] = BoolGetDatum(fc_granted);
		fc_values[14] = BoolGetDatum(fc_instance->fastpath);
		if (!fc_granted && fc_instance->waitStart != 0)
			fc_values[15] = TimestampTzGetDatum(fc_instance->waitStart);
		else
			fc_nulls[15] = true;

		fc_tuple = heap_form_tuple(fc_funcctx->tuple_desc, fc_values, fc_nulls);
		fc_result = HeapTupleGetDatum(fc_tuple);
		SRF_RETURN_NEXT(fc_funcctx, fc_result);
	}

	/*
	 * 已经返回所有常规锁。现在开始处理 SIREAD 预测
	 * 锁。
	 */
	fc_predLockData = fc_mystatus->predLockData;
	if (fc_mystatus->predLockIdx < fc_predLockData->nelements)
	{
		PredicateLockTargetType fc_lockType;

		PREDICATELOCKTARGETTAG *fc_predTag = &(fc_predLockData->locktags[fc_mystatus->predLockIdx]);
		SERIALIZABLEXACT *fc_xact = &(fc_predLockData->xacts[fc_mystatus->predLockIdx]);
		Datum		fc_values[NUM_LOCK_STATUS_COLUMNS];
		bool		fc_nulls[NUM_LOCK_STATUS_COLUMNS];
		HeapTuple	fc_tuple;
		Datum		fc_result;

		fc_mystatus->predLockIdx++;

		/*
		 * 使用适当的数据形成元组。
		 */
		MemSet(fc_values, 0, sizeof(fc_values));
		MemSet(fc_nulls, false, sizeof(fc_nulls));

		/* 锁类型 */
		fc_lockType = GET_PREDICATELOCKTARGETTAG_TYPE(*fc_predTag);

		fc_values[0] = CStringGetTextDatum(PredicateLockTagTypeNames[fc_lockType]);

		/* 锁目标 */
		fc_values[1] = GET_PREDICATELOCKTARGETTAG_DB(*fc_predTag);
		fc_values[2] = GET_PREDICATELOCKTARGETTAG_RELATION(*fc_predTag);
		if (fc_lockType == PREDLOCKTAG_TUPLE)
			fc_values[4] = GET_PREDICATELOCKTARGETTAG_OFFSET(*fc_predTag);
		else
			fc_nulls[4] = true;
		if ((fc_lockType == PREDLOCKTAG_TUPLE) ||
			(fc_lockType == PREDLOCKTAG_PAGE))
			fc_values[3] = GET_PREDICATELOCKTARGETTAG_PAGE(*fc_predTag);
		else
			fc_nulls[3] = true;

		/* 这些字段是其他类型锁的目标 */
		fc_nulls[5] = true;		/* virtualxid */
		fc_nulls[6] = true;		/* 事务ID */
		fc_nulls[7] = true;		/* classid */
		fc_nulls[8] = true;		/* objid */
		fc_nulls[9] = true;		/* objsubid */

		/* 锁持有者 */
		fc_values[10] = fc_VXIDGetDatum(fc_xact->vxid.backendId,
								  fc_xact->vxid.localTransactionId);
		if (fc_xact->pid != 0)
			fc_values[11] = Int32GetDatum(fc_xact->pid);
		else
			fc_nulls[11] = true;

		/*
		 * 锁模式。目前所有的谓词锁都是SIReadLocks，它们
		 * 始终被持有（从不等待）并且没有快捷路径
		 */
		fc_values[12] = CStringGetTextDatum("SIReadLock");
		fc_values[13] = BoolGetDatum(true);
		fc_values[14] = BoolGetDatum(false);
		fc_nulls[15] = true;

		fc_tuple = heap_form_tuple(fc_funcctx->tuple_desc, fc_values, fc_nulls);
		fc_result = HeapTupleGetDatum(fc_tuple);
		SRF_RETURN_NEXT(fc_funcctx, fc_result);
	}

	SRF_RETURN_DONE(fc_funcctx);
}


/*
 * pg_blocking_pids - 生成一个阻塞给定PID的PID数组
 *
 * 所报告的PID是那些与blocked_pid当前请求冲突的持锁PID
 * （硬阻塞），或请求此类锁且在锁的等待队列中位于blocked_pid之前的PID
 * （软阻塞）。
 *
 * 在并行查询情况下，我们报告所有阻塞给定PID的锁组成员的PID，
 * 报告的PID是阻塞PID的锁组领导者的PID。这允许调用者
 * 在并行查询期间将结果与客户端的pg_backend_pid()结果进行比较。
 *
 * 并行查询使得结果中可能出现重复的PID
 * （要么是多个等待者被同一个PID阻塞，或者
 * 是多个阻塞者具有相同的组领导者PID）。我们不想
 * 删除结果中的这种重复。
 *
 * 在这里我们不需要考虑谓词锁，因为它们不会阻塞任何东西。
 */
Datum pg_blocking_pids(PG_FUNCTION_ARGS)
{
	int			fc_blocked_pid = PG_GETARG_INT32(0);
	Datum	   *fc_arrayelems;
	int			fc_narrayelems;
	BlockedProcsData *fc_lockData; /* 来自 lmgr 的状态数据 */
	int			fc_i,
				fc_j;

	/* 收集锁管理器状态的快照 */
	fc_lockData = GetBlockerStatusData(fc_blocked_pid);

	/* 我们所需的输出条目不能超过报告的PROCLOCK数量 */
	fc_arrayelems = (Datum *) palloc(fc_lockData->nlocks * sizeof(Datum));
	fc_narrayelems = 0;

	/* 对于锁组中的每个被阻塞进程... */
	for (fc_i = 0; fc_i < fc_lockData->nprocs; fc_i++)
	{
		BlockedProcData *fc_bproc = &fc_lockData->procs[fc_i];
		LockInstanceData *fc_instances = &fc_lockData->locks[fc_bproc->first_lock];
		int		   *fc_preceding_waiters = &fc_lockData->waiter_pids[fc_bproc->first_waiter];
		LockInstanceData *fc_blocked_instance;
		LockMethod	fc_lockMethodTable;
		int			fc_conflictMask;

		/*
		 * 定位被阻塞进程在LockInstanceData数组中的自身条目。
		 * 应该恰好有一个匹配条目。
		 */
		fc_blocked_instance = NULL;
		for (fc_j = 0; fc_j < fc_bproc->num_locks; fc_j++)
		{
			LockInstanceData *fc_instance = &(fc_instances[fc_j]);

			if (fc_instance->pid == fc_bproc->pid)
			{
				Assert(fc_blocked_instance == NULL);
				fc_blocked_instance = fc_instance;
			}
		}
		Assert(fc_blocked_instance != NULL);

		fc_lockMethodTable = GetLockTagsMethodTable(&(fc_blocked_instance->locktag));
		fc_conflictMask = fc_lockMethodTable->conflictTab[fc_blocked_instance->waitLockMode];

		/* 现在扫描PROCLOCK数据以查找冲突进程 */
		for (fc_j = 0; fc_j < fc_bproc->num_locks; fc_j++)
		{
			LockInstanceData *fc_instance = &(fc_instances[fc_j]);

			/* 进程永远不会阻塞自己，因此忽略该条目 */
			if (fc_instance == fc_blocked_instance)
				continue;
			/* 同一锁组的成员也不会互相阻塞 */
			if (fc_instance->leaderPid == fc_blocked_instance->leaderPid)
				continue;

			if (fc_conflictMask & fc_instance->holdMask)
			{
				/* 硬阻塞：被此条目已持有的锁阻塞 */
			}
			else if (fc_instance->waitLockMode != NoLock &&
					 (fc_conflictMask & LOCKBIT_ON(fc_instance->waitLockMode)))
			{
				/* 锁请求的冲突；谁在等待队列中位于前面？ */
				bool		fc_ahead = false;
				int			fc_k;

				for (fc_k = 0; fc_k < fc_bproc->num_waiters; fc_k++)
				{
					if (fc_preceding_waiters[fc_k] == fc_instance->pid)
					{
						/* 软阻塞：此条目在被阻塞的进程之前 */
						fc_ahead = true;
						break;
					}
				}
				if (!fc_ahead)
					continue;	/* 不是被此条目阻塞 */
			}
			else
			{
				/* 不是被此条目阻塞 */
				continue;
			}

			/* 被此条目阻塞，因此发出记录 */
			fc_arrayelems[fc_narrayelems++] = Int32GetDatum(fc_instance->leaderPid);
		}
	}

	/* 确保我们没有超出arrayelems[] */
	Assert(fc_narrayelems <= fc_lockData->nlocks);

	/* 构建数组，使用对int4类型的硬编码知识 */
	PG_RETURN_ARRAYTYPE_P(construct_array(fc_arrayelems, fc_narrayelems,
										  INT4OID,
										  sizeof(int32), true, TYPALIGN_INT));
}


/*
 * pg_safe_snapshot_blocking_pids - 生成一个阻塞
 * 给定PID获取安全快照的PID数组
 *
 * XXX 这不考虑并行查询情况；实际中这究竟是一个
 * 多大的问题尚不清楚
 */
Datum pg_safe_snapshot_blocking_pids(PG_FUNCTION_ARGS)
{
	int			fc_blocked_pid = PG_GETARG_INT32(0);
	int		   *fc_blockers;
	int			fc_num_blockers;
	Datum	   *fc_blocker_datums;

	/* 足够大的缓冲区以容纳任何可能的阻塞者列表而不截断 */
	fc_blockers = (int *) palloc(MaxBackends * sizeof(int));

	/* 收集GetSafeSnapshot等待的进程快照 */
	fc_num_blockers =
		GetSafeSnapshotBlockingPids(fc_blocked_pid, fc_blockers, MaxBackends);

	/* 将int数组转换为Datum数组 */
	if (fc_num_blockers > 0)
	{
		int			fc_i;

		fc_blocker_datums = (Datum *) palloc(fc_num_blockers * sizeof(Datum));
		for (fc_i = 0; fc_i < fc_num_blockers; ++fc_i)
			fc_blocker_datums[fc_i] = Int32GetDatum(fc_blockers[fc_i]);
	}
	else
		fc_blocker_datums = NULL;

	/* 构建数组，使用对int4类型的硬编码知识 */
	PG_RETURN_ARRAYTYPE_P(construct_array(fc_blocker_datums, fc_num_blockers,
										  INT4OID,
										  sizeof(int32), true, TYPALIGN_INT));
}


/*
 * pg_isolation_test_session_is_blocked - isolationtester的支持函数
 *
 * 检查指定PID是否被第二个参数中列出的任何PID阻塞。
 * 目前，这查找由于等待
 * 重型锁或安全快照而导致的阻塞。我们忽略由不直接
 * 在isolationtester控制下的PID导致的阻塞，例如autovacuum。
 *
 * 这是一个未文档化的函数，仅供隔离测试器使用，
 * 未来版本中可能会根据测试需要进行更改。
 */
Datum pg_isolation_test_session_is_blocked(PG_FUNCTION_ARGS)
{
	int			fc_blocked_pid = PG_GETARG_INT32(0);
	ArrayType  *fc_interesting_pids_a = PG_GETARG_ARRAYTYPE_P(1);
	ArrayType  *fc_blocking_pids_a;
	int32	   *fc_interesting_pids;
	int32	   *fc_blocking_pids;
	int			fc_num_interesting_pids;
	int			fc_num_blocking_pids;
	int			fc_dummy;
	int			fc_i,
				fc_j;

	/* 验证传入的数组 */
	Assert(ARR_ELEMTYPE(fc_interesting_pids_a) == INT4OID);
	if (array_contains_nulls(fc_interesting_pids_a))
		elog(ERROR, "array must not contain nulls");
	fc_interesting_pids = (int32 *) ARR_DATA_PTR(fc_interesting_pids_a);
	fc_num_interesting_pids = ArrayGetNItems(ARR_NDIM(fc_interesting_pids_a),
										  ARR_DIMS(fc_interesting_pids_a));

	/*
	 * 获取所有阻塞给定会话获取重型锁的会话的PID。
	 */
	fc_blocking_pids_a =
		DatumGetArrayTypeP(DirectFunctionCall1(pg_blocking_pids, fc_blocked_pid));

	Assert(ARR_ELEMTYPE(fc_blocking_pids_a) == INT4OID);
	Assert(!array_contains_nulls(fc_blocking_pids_a));
	fc_blocking_pids = (int32 *) ARR_DATA_PTR(fc_blocking_pids_a);
	fc_num_blocking_pids = ArrayGetNItems(ARR_NDIM(fc_blocking_pids_a),
									   ARR_DIMS(fc_blocking_pids_a));

	/*
	 * 检查这些PID是否在有趣PID的列表中，即
	 * 隔离测试器正在运行的会话。我们不在这里使用
	 * "arrayoverlaps"，因为这会导致缓存查找，我们的一个
	 * 目标是以debug_discard_caches > 0的情况下快速运行。我们预期
	 * blocking_pids通常是空的，否则在隔离测试者的情况下是一个非常小的数字，因此将其设置为外层循环以进行匹配的简单搜索。
	 */
	for (fc_i = 0; fc_i < fc_num_blocking_pids; fc_i++)
		for (fc_j = 0; fc_j < fc_num_interesting_pids; fc_j++)
		{
			if (fc_blocking_pids[fc_i] == fc_interesting_pids[fc_j])
				PG_RETURN_BOOL(true);
		}

	/*
	 * 检查blocked_pid是否在等待安全快照。理论上我们可以将
	 * 阻塞者PID的结果数组与有趣的PID列表进行比较，但由于
	 * autovacuum不会阻塞GetSafeSnapshot，因此似乎没有必要
	 * 在分配缓冲区和搜索重叠上浪费周期；所以目前
	 * 对隔离测试器的目的来说，使用单个元素缓冲区并检查
	 * 安全快照阻塞者的数量是否为非零就足够了。
	 */
	if (GetSafeSnapshotBlockingPids(fc_blocked_pid, &fc_dummy, 1) > 0)
		PG_RETURN_BOOL(true);

	PG_RETURN_BOOL(false);
}


/*
 * 操作顾问锁的函数
 *
 * 我们利用锁标记字段如下：
 *
 *	field1：MyDatabaseId ... 确保锁是本地于每个数据库
 *	field2：两个int4键的第一个，或int8键的高位半部分
 *	field3：两个int4键的第二个，或int8键的低位半部分
 *	field4：如果使用int8键则为1，如果使用两个int4键则为2
 */
#define SET_LOCKTAG_INT64(tag, key64) \
	SET_LOCKTAG_ADVISORY(tag, \
						 MyDatabaseId, \
						 (uint32) ((key64) >> 32), \
						 (uint32) (key64), \
						 1)
#define SET_LOCKTAG_INT32(tag, key1, key2) \
	SET_LOCKTAG_ADVISORY(tag, MyDatabaseId, key1, key2, 2)

/*
 * pg_advisory_lock(int8) - 在int8键上获取排他锁
 */
Datum pg_advisory_lock_int8(PG_FUNCTION_ARGS)
{
	int64		fc_key = PG_GETARG_INT64(0);
	LOCKTAG		fc_tag;

	SET_LOCKTAG_INT64(fc_tag, fc_key);

	(void) LockAcquire(&fc_tag, ExclusiveLock, true, false);

	PG_RETURN_VOID();
}

/*
 * pg_advisory_xact_lock(int8) - 在int8键上获取事务范围
 * 排他锁
 */
Datum pg_advisory_xact_lock_int8(PG_FUNCTION_ARGS)
{
	int64		fc_key = PG_GETARG_INT64(0);
	LOCKTAG		fc_tag;

	SET_LOCKTAG_INT64(fc_tag, fc_key);

	(void) LockAcquire(&fc_tag, ExclusiveLock, false, false);

	PG_RETURN_VOID();
}


/*
 * pg_advisory_lock_shared(int8) - 在 int8 键上获取共享锁
 */
Datum pg_advisory_lock_shared_int8(PG_FUNCTION_ARGS)
{
	int64		fc_key = PG_GETARG_INT64(0);
	LOCKTAG		fc_tag;

	SET_LOCKTAG_INT64(fc_tag, fc_key);

	(void) LockAcquire(&fc_tag, ShareLock, true, false);

	PG_RETURN_VOID();
}

/*
 * pg_advisory_xact_lock_shared(int8) - 在 int8 键上获取事务范围的
 * 共享锁
 */
Datum pg_advisory_xact_lock_shared_int8(PG_FUNCTION_ARGS)
{
	int64		fc_key = PG_GETARG_INT64(0);
	LOCKTAG		fc_tag;

	SET_LOCKTAG_INT64(fc_tag, fc_key);

	(void) LockAcquire(&fc_tag, ShareLock, false, false);

	PG_RETURN_VOID();
}

/*
 * pg_try_advisory_lock(int8) - 在 int8 键上获取排他锁，不等待
 *
 * 成功时返回 true，锁不可用时返回 false
 */
Datum pg_try_advisory_lock_int8(PG_FUNCTION_ARGS)
{
	int64		fc_key = PG_GETARG_INT64(0);
	LOCKTAG		fc_tag;
	LockAcquireResult fc_res;

	SET_LOCKTAG_INT64(fc_tag, fc_key);

	fc_res = LockAcquire(&fc_tag, ExclusiveLock, true, true);

	PG_RETURN_BOOL(fc_res != LOCKACQUIRE_NOT_AVAIL);
}

/*
 * pg_try_advisory_xact_lock(int8) - 在 int8 键上获取事务范围的
 * 排他锁，不等待
 *
 * 成功时返回 true，锁不可用时返回 false
 */
Datum pg_try_advisory_xact_lock_int8(PG_FUNCTION_ARGS)
{
	int64		fc_key = PG_GETARG_INT64(0);
	LOCKTAG		fc_tag;
	LockAcquireResult fc_res;

	SET_LOCKTAG_INT64(fc_tag, fc_key);

	fc_res = LockAcquire(&fc_tag, ExclusiveLock, false, true);

	PG_RETURN_BOOL(fc_res != LOCKACQUIRE_NOT_AVAIL);
}

/*
 * pg_try_advisory_lock_shared(int8) - 在 int8 键上获取共享锁，不等待
 *
 * 成功时返回 true，锁不可用时返回 false
 */
Datum pg_try_advisory_lock_shared_int8(PG_FUNCTION_ARGS)
{
	int64		fc_key = PG_GETARG_INT64(0);
	LOCKTAG		fc_tag;
	LockAcquireResult fc_res;

	SET_LOCKTAG_INT64(fc_tag, fc_key);

	fc_res = LockAcquire(&fc_tag, ShareLock, true, true);

	PG_RETURN_BOOL(fc_res != LOCKACQUIRE_NOT_AVAIL);
}

/*
 * pg_try_advisory_xact_lock_shared(int8) - 在 int8 键上获取事务范围的
 * 共享锁，不等待
 *
 * 成功时返回 true，锁不可用时返回 false
 */
Datum pg_try_advisory_xact_lock_shared_int8(PG_FUNCTION_ARGS)
{
	int64		fc_key = PG_GETARG_INT64(0);
	LOCKTAG		fc_tag;
	LockAcquireResult fc_res;

	SET_LOCKTAG_INT64(fc_tag, fc_key);

	fc_res = LockAcquire(&fc_tag, ShareLock, false, true);

	PG_RETURN_BOOL(fc_res != LOCKACQUIRE_NOT_AVAIL);
}

/*
 * pg_advisory_unlock(int8) - 释放在 int8 键上的排他锁
 *
 * 成功时返回 true，锁未被持有时返回 false
*/
Datum pg_advisory_unlock_int8(PG_FUNCTION_ARGS)
{
	int64		fc_key = PG_GETARG_INT64(0);
	LOCKTAG		fc_tag;
	bool		fc_res;

	SET_LOCKTAG_INT64(fc_tag, fc_key);

	fc_res = LockRelease(&fc_tag, ExclusiveLock, true);

	PG_RETURN_BOOL(fc_res);
}

/*
 * pg_advisory_unlock_shared(int8) - 释放在 int8 键上的共享锁
 *
 * 成功时返回 true，锁未被持有时返回 false
 */
Datum pg_advisory_unlock_shared_int8(PG_FUNCTION_ARGS)
{
	int64		fc_key = PG_GETARG_INT64(0);
	LOCKTAG		fc_tag;
	bool		fc_res;

	SET_LOCKTAG_INT64(fc_tag, fc_key);

	fc_res = LockRelease(&fc_tag, ShareLock, true);

	PG_RETURN_BOOL(fc_res);
}

/*
 * pg_advisory_lock(int4, int4) - 在 2 个 int4 键上获取排他锁
 */
Datum pg_advisory_lock_int4(PG_FUNCTION_ARGS)
{
	int32		fc_key1 = PG_GETARG_INT32(0);
	int32		fc_key2 = PG_GETARG_INT32(1);
	LOCKTAG		fc_tag;

	SET_LOCKTAG_INT32(fc_tag, fc_key1, fc_key2);

	(void) LockAcquire(&fc_tag, ExclusiveLock, true, false);

	PG_RETURN_VOID();
}

/*
 * pg_advisory_xact_lock(int4, int4) - 在 2 个 int4 键上获取事务范围的
 * 排他锁
 */
Datum pg_advisory_xact_lock_int4(PG_FUNCTION_ARGS)
{
	int32		fc_key1 = PG_GETARG_INT32(0);
	int32		fc_key2 = PG_GETARG_INT32(1);
	LOCKTAG		fc_tag;

	SET_LOCKTAG_INT32(fc_tag, fc_key1, fc_key2);

	(void) LockAcquire(&fc_tag, ExclusiveLock, false, false);

	PG_RETURN_VOID();
}

/*
 * pg_advisory_lock_shared(int4, int4) - 在 2 个 int4 键上获取共享锁
 */
Datum pg_advisory_lock_shared_int4(PG_FUNCTION_ARGS)
{
	int32		fc_key1 = PG_GETARG_INT32(0);
	int32		fc_key2 = PG_GETARG_INT32(1);
	LOCKTAG		fc_tag;

	SET_LOCKTAG_INT32(fc_tag, fc_key1, fc_key2);

	(void) LockAcquire(&fc_tag, ShareLock, true, false);

	PG_RETURN_VOID();
}

/*
 * pg_advisory_xact_lock_shared(int4, int4) - 在 2 个 int4 键上获取事务范围的
 * 共享锁
 */
Datum pg_advisory_xact_lock_shared_int4(PG_FUNCTION_ARGS)
{
	int32		fc_key1 = PG_GETARG_INT32(0);
	int32		fc_key2 = PG_GETARG_INT32(1);
	LOCKTAG		fc_tag;

	SET_LOCKTAG_INT32(fc_tag, fc_key1, fc_key2);

	(void) LockAcquire(&fc_tag, ShareLock, false, false);

	PG_RETURN_VOID();
}

/*
 * pg_try_advisory_lock(int4, int4) - 在 2 个 int4 键上获取排他锁，不等待
 *
 * 成功时返回 true，锁不可用时返回 false
 */
Datum pg_try_advisory_lock_int4(PG_FUNCTION_ARGS)
{
	int32		fc_key1 = PG_GETARG_INT32(0);
	int32		fc_key2 = PG_GETARG_INT32(1);
	LOCKTAG		fc_tag;
	LockAcquireResult fc_res;

	SET_LOCKTAG_INT32(fc_tag, fc_key1, fc_key2);

	fc_res = LockAcquire(&fc_tag, ExclusiveLock, true, true);

	PG_RETURN_BOOL(fc_res != LOCKACQUIRE_NOT_AVAIL);
}

/*
 * pg_try_advisory_xact_lock(int4, int4) - 在 2 个 int4 键上获取事务范围的
 * 排他锁，不等待
 *
 * 成功时返回 true，锁不可用时返回 false
 */
Datum pg_try_advisory_xact_lock_int4(PG_FUNCTION_ARGS)
{
	int32		fc_key1 = PG_GETARG_INT32(0);
	int32		fc_key2 = PG_GETARG_INT32(1);
	LOCKTAG		fc_tag;
	LockAcquireResult fc_res;

	SET_LOCKTAG_INT32(fc_tag, fc_key1, fc_key2);

	fc_res = LockAcquire(&fc_tag, ExclusiveLock, false, true);

	PG_RETURN_BOOL(fc_res != LOCKACQUIRE_NOT_AVAIL);
}

/*
 * pg_try_advisory_lock_shared(int4, int4) - 在 2 个 int4 键上获取共享锁，不等待
 *
 * 成功时返回 true，锁不可用时返回 false
 */
Datum pg_try_advisory_lock_shared_int4(PG_FUNCTION_ARGS)
{
	int32		fc_key1 = PG_GETARG_INT32(0);
	int32		fc_key2 = PG_GETARG_INT32(1);
	LOCKTAG		fc_tag;
	LockAcquireResult fc_res;

	SET_LOCKTAG_INT32(fc_tag, fc_key1, fc_key2);

	fc_res = LockAcquire(&fc_tag, ShareLock, true, true);

	PG_RETURN_BOOL(fc_res != LOCKACQUIRE_NOT_AVAIL);
}

/*
 * pg_try_advisory_xact_lock_shared(int4, int4) - 在 2 个 int4 键上获取事务范围的
 * 共享锁，不等待
 *
 * 成功时返回 true，锁不可用时返回 false
 */
Datum pg_try_advisory_xact_lock_shared_int4(PG_FUNCTION_ARGS)
{
	int32		fc_key1 = PG_GETARG_INT32(0);
	int32		fc_key2 = PG_GETARG_INT32(1);
	LOCKTAG		fc_tag;
	LockAcquireResult fc_res;

	SET_LOCKTAG_INT32(fc_tag, fc_key1, fc_key2);

	fc_res = LockAcquire(&fc_tag, ShareLock, false, true);

	PG_RETURN_BOOL(fc_res != LOCKACQUIRE_NOT_AVAIL);
}

/*
 * pg_advisory_unlock(int4, int4) - 释放在 2 个 int4 键上的排他锁
 *
 * 成功时返回 true，锁未被持有时返回 false
*/
Datum pg_advisory_unlock_int4(PG_FUNCTION_ARGS)
{
	int32		fc_key1 = PG_GETARG_INT32(0);
	int32		fc_key2 = PG_GETARG_INT32(1);
	LOCKTAG		fc_tag;
	bool		fc_res;

	SET_LOCKTAG_INT32(fc_tag, fc_key1, fc_key2);

	fc_res = LockRelease(&fc_tag, ExclusiveLock, true);

	PG_RETURN_BOOL(fc_res);
}

/*
 * pg_advisory_unlock_shared(int4, int4) - 释放在 2 个 int4 键上的共享锁
 *
 * 成功时返回 true，锁未被持有时返回 false
 */
Datum pg_advisory_unlock_shared_int4(PG_FUNCTION_ARGS)
{
	int32		fc_key1 = PG_GETARG_INT32(0);
	int32		fc_key2 = PG_GETARG_INT32(1);
	LOCKTAG		fc_tag;
	bool		fc_res;

	SET_LOCKTAG_INT32(fc_tag, fc_key1, fc_key2);

	fc_res = LockRelease(&fc_tag, ShareLock, true);

	PG_RETURN_BOOL(fc_res);
}

/*
 * pg_advisory_unlock_all() - 释放所有建议锁
 */
Datum pg_advisory_unlock_all(PG_FUNCTION_ARGS)
{
	LockReleaseSession(USER_LOCKMETHOD);

	PG_RETURN_VOID();
}
